﻿package
{
	import flash.display.Sprite;
	import flash.display.DisplayObject;
	import flash.display.BlendMode;
	
	import flash.events.Event;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	
	import flash.filters.BlurFilter;
	import flash.filters.DisplacementMapFilter;
	import flash.filters.DisplacementMapFilterMode;
	import flash.filters.BitmapFilter;
	
	public class DiffuseEffect extends Sprite
	{
		//VARIABLES INTERNAS SETEABLES OBLIGATORIAS.
			
			public var diffuseClip:Sprite;
			
		//VARIABLES INTERNAS SETEABLES OPCIONALES.
		
			//GENERAL.
			public var activeTracing:Boolean = true;
			
			//COLOR.
			public var rTransform:Number = 1;
			public var gTransform:Number = 1;
			public var bTransform:Number = 1;
			public var rOffset:Number = 0;
			public var gOffset:Number = 0;
			public var bOffset:Number = 0;
			
			//RUIDO (TEXTURA/FORMA).
			public var noiseAlpha:Number = 0.5;
			public var noiseWidth:Number = 50;
			public var noiseHeight:Number = 100;
			public var noiseOctaves:Number = 16;
			
			//BLUR.
			public var blurStrength:Number = 10;
			
			//DESPLAZAMIENTO.
			public var displaceFactor:Number = 25;
		
		//VARIABLES INTERNAS.
			
			private var rootClip:Sprite;
			private var clipsChained:Array = [];
			private var clipsOver:Array = [];
			private var targetBmpDataHolder:Sprite = new Sprite();
			private var targetBmpDataHolderBmp:Bitmap = new Bitmap();
			private var diffuseBmpDataHolder:Sprite = new Sprite();
			private var diffuseBmpDataHolderBmp:Bitmap = new Bitmap();
			private var noiseBmpDataHolder:Sprite = new Sprite();
			private var noiseBmpDataHolderBmp:Bitmap = new Bitmap();
		
		//CONSTRUCTOR.
		public function  DiffuseEffect()
		{
			
		}
		
		//INIT.
		public function init():void
		{
			//INICIALIZA LOS HOLDERS.
			diffuseClip.addChild(targetBmpDataHolder);
			diffuseClip.addChild(diffuseBmpDataHolder);
			diffuseClip.addChild(noiseBmpDataHolder);
			targetBmpDataHolder.addChild(targetBmpDataHolderBmp);
			diffuseBmpDataHolder.addChild(diffuseBmpDataHolderBmp);
			noiseBmpDataHolder.addChild(noiseBmpDataHolderBmp);
			noiseBmpDataHolder.blendMode = BlendMode.OVERLAY;
			noiseBmpDataHolder.alpha = noiseAlpha;
			
			//ANALIZA EL DISPLAY TREE.
			clipsChained.push(diffuseClip);
			findRootClip(diffuseClip);
			findOverClips();
			
			//REDIBUJA EL CLIP DE DIFUSION.
			redrawDiffuseClip();
			
			//DIBUJA EL NOISE.
			drawNoise();
			
			//EVALUA SI REDIBUJA CONSTANTEMENTE O SOLO UNA VEZ.
			if(activeTracing)
				addEventListener(Event.ENTER_FRAME, drawDiffusion);
			else
				drawDiffusion(null);
		}
		
		//REDIBUJA CON EFECTO DE DIFUMINADO.
		private function drawDiffusion(evt:Event):void
		{
			//ESCONDE QUE ESTAN POR ENCIMA DEL DIFFUSECLIP.
			showOverClips(false);
			diffuseClip.visible = false;
			
			//REDIBUJA.
			var bmpData:BitmapData = new BitmapData(diffuseClip.width, diffuseClip.height, true, 0x00000000);
			var mat:Matrix = new Matrix();
			mat.translate(-diffuseClip.x - diffuseClip.parent.x, -diffuseClip.y - diffuseClip.parent.y);
			bmpData.draw(rootClip, mat, new ColorTransform(rTransform, gTransform, bTransform, 1, rOffset, gOffset, bOffset, 0));
			
			//FILTRO: MAPA DE DESPLAZAMIENTO.
            var dm:DisplacementMapFilter = new DisplacementMapFilter(noiseBmpDataHolderBmp.bitmapData, new Point(), BitmapDataChannel.RED, BitmapDataChannel.RED, displaceFactor, displaceFactor, DisplacementMapFilterMode.CLAMP, 0, 0);

			//FILTRO: BLUR.
			var blur:BlurFilter = new BlurFilter(blurStrength, blurStrength, 3);
			
			//APLICA LOS FILTROS.
			bmpData.applyFilter(bmpData, bmpData.rect, new Point(), dm);
			bmpData.applyFilter(bmpData, bmpData.rect, new Point(), blur);
			
			//ACTUALIZA.
			targetBmpDataHolderBmp.bitmapData = bmpData;
			
			//MUESTRA TODOS LOS CLIPS.
			showOverClips(true);
			diffuseClip.visible = true;
		}
		
		//HIDECLIPS.
		private function showOverClips(bool:Boolean):void
		{
			for(var i:uint; i<clipsOver.length; i++)
				clipsOver[i].visible = bool;
		}
		
		//FINDOVERCLIPS.
		private function findOverClips():void
		{
			for(var i:uint; i<clipsChained.length-1; i++)
			{
				//IDENTIFICA.
				var thisSpr:Sprite = clipsChained[i];
				var parentSpr:Sprite = clipsChained[i+1];
				var thisSprIndex:uint = parentSpr.getChildIndex(thisSpr);
				
				//ENCUENTRA LOS CLIPS SOBRE EL CLIP.
				for(var j:uint = 0; j<parentSpr.numChildren; j++)
				{
					var spr:Sprite = Sprite(parentSpr.getChildAt(j));
					if(spr != clipsChained[i] && j > thisSprIndex)
						clipsOver.push(spr);
				}
			}
		}
		
		//FINDROOT.
		private function findRootClip(spr:Sprite):void
		{
			var parentSpr:Sprite = Sprite(spr.parent);
			clipsChained.push(parentSpr);
			if(parentSpr.name == "root1")
				rootClip = parentSpr;
			else
				findRootClip(parentSpr);
		}
		
		//REDRAWDIFFUSECLIP.
		private function redrawDiffuseClip():void
		{
			//REDIBUJA.
			var bmpData:BitmapData = new BitmapData(diffuseClip.width, diffuseClip.height, true, 0x00000000);
			bmpData.draw(diffuseClip);
			
			//ACTUALIZA.
			diffuseBmpDataHolderBmp.bitmapData = bmpData;
		}
		
		//DRAWNOISE.		
		private function drawNoise():void
		{
			//REDIBUJA.
			var bmpData:BitmapData = new BitmapData(diffuseClip.width, diffuseClip.height, true, 0x00000000);
			bmpData.perlinNoise(noiseWidth, noiseHeight, noiseOctaves, 1, false, true, BitmapDataChannel.RED, true);
			
			//ACTUALIZA.
			noiseBmpDataHolderBmp.bitmapData = bmpData;
		}
	}
}